package evs.iap;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URISyntaxException;

import evs.brp.RemotingErrorException;


public class ServerRequestHandler extends evs.brp.ServerRequestHandler {
	
	protected Invoker invoker; 
	
	public ServerRequestHandler(int pPort, String nameServerUri) throws RemotingErrorException {
		super(pPort);
		
		try {
			invoker = new Invoker(nameServerUri);
		}catch (URISyntaxException ue) {
			throw new RemotingErrorException(ue);
		}
			
	}

	@Override
	public void handleClient(Socket client) {	
		log.info("handling accepted client socket");    
		new Handler(this, client).start();
	}
	
	class Daemon extends Thread {

		private ServerSocket serverSocket;
		private ServerRequestHandler serverRequestHandler;
		private int port;
		
		public Daemon(ServerRequestHandler serverRequestHandler, int pPort) {
			log.info("constructing new daemon thread..");
			this.serverRequestHandler = serverRequestHandler;
			this.port = pPort;
		}
		
		public void run() {
			try {
				log.info("creating a server socket to accept connections..");
				
				serverSocket = new ServerSocket(port);
			}
			catch (Exception e) {
				log.error("Problem establishing server socket: " + e);
				return;
			}

			try {
				while (true) {
			        try {
			    		log.info("wait for client to connect...");  
			        	serverRequestHandler.handleClient(serverSocket.accept());
			        }
			        catch (IOException ioe) {
			        	log.error("Problem accepting client's socket connection: " + ioe);
			        }
				}
			}
			catch (ThreadDeath e) {
				// When the thread is killed, close the server socket
				try {
					serverSocket.close();
				}
				catch (IOException ioe) {
					log.error("Problem closing server socket: " + ioe);
				}
			}
		}
	}
	
	
	class Handler extends Thread {

		private Socket clientSocket;
		private ServerRequestHandler serverRequestHandler;
		
		public Handler(ServerRequestHandler serverRequestHandler, Socket clientSocket) {
			log.info("constructing new handler thread..");
			this.serverRequestHandler = serverRequestHandler;
			this.clientSocket = clientSocket;
		}
		
		public void run() {
			try {			
				// get Input stream
				log.info("get inputstream..");
				InputStream inputStream = clientSocket.getInputStream();
				ObjectInput objectInput = new ObjectInputStream(inputStream);

				// get Output stream
				log.info("get outputstream..");
				OutputStream outputStream = clientSocket.getOutputStream();
				ObjectOutput objectOutput = new ObjectOutputStream(outputStream);
				
				/*
				 * delegate streams to Invoker
				 */
				log.info("delegate to invoker..");
				serverRequestHandler.invoker.delegate(objectInput, objectOutput);
				
				objectInput.close();
				objectOutput.close();
		    } 
		    catch (Exception e) {
				log.error(e.getMessage());
				e.printStackTrace();
		    }
		}
	}	
}
